home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / telecomm / sticpsrc.lzh / SOURCE.ARC / FTPCLI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-08-10  |  18.2 KB  |  784 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "icmp.h"
  7. #include "timer.h"
  8. #include "tcp.h"
  9. #include "ftp.h"
  10. #include "session.h"
  11. #include "cmdparse.h"
  12. #ifdef ATARI_ST
  13. # ifdef __TURBOC__
  14. #  include <tos.h>
  15. # else
  16. #  include <osbind.h>
  17. # endif
  18. #endif
  19.  
  20. extern struct session *current;
  21. extern char nospace[];
  22. extern char badhost[];
  23. static char notsess[] = "Not an FTP session!\n";
  24. static char cantwrite[] = "Can't write %s\n";
  25. static char cantread[] = "Can't read %s\n";
  26.  
  27. #if (defined(MSC) || (defined(ATARI_ST) && defined(__TURBOC__)))
  28. static int sndftpmsg(struct ftp *ftp,char *fmt,...);
  29. #endif
  30.  
  31. int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
  32.     domkdir(),dormdir(),dotput();
  33.  
  34. struct cmds ftpabort[] = {
  35.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  36.     "abort",    doabort,    0,    NULLCHAR,        NULLCHAR,
  37.     NULLCHAR,    NULLFP,        0,    "Only valid command is \"abort\"", NULLCHAR
  38. };
  39.  
  40. struct cmds ftpcmds[] = {
  41.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  42.     "cd",        doftpcd,    2,    "<directory>",        NULLCHAR,
  43.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  44.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  45.     "get",        doget,        2,    "<remotefile> [<localfile>]",    NULLCHAR,
  46.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  47.     "mkdir",    domkdir,    2,    "<directory>",        NULLCHAR,
  48.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  49.     "rmdir",    dormdir,    2,    "<directory>",        NULLCHAR,
  50.     "put",        doput,        2,    "<localfile> [<remotefile>]",    NULLCHAR,
  51.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  52. #if (defined(MSDOS))
  53.     "tput",        dotput,        2,    "<localspec>",        NULLCHAR,
  54. #endif
  55. #if (defined(ATARI_ST))
  56.     "tput",        dotput,        2,    "<localspec> [x]",    NULLCHAR,
  57. #endif
  58.     NULLCHAR,    NULLFP,        0,    NULLCHAR,        NULLCHAR
  59. };
  60.  
  61. /* Handle top-level FTP command */
  62. doftp(argc,argv)
  63. int argc;
  64. char *argv[];
  65. {
  66.     int32 resolve();
  67.     int ftpparse();
  68.     void ftpccr(),ftpccs();
  69.     struct session *s;
  70.     struct ftp *ftp,*ftp_create();
  71.     struct tcb *tcb;
  72.     struct socket lsocket,fsocket;
  73.     char tos = 0;
  74.  
  75.     lsocket.address = ip_addr;
  76.     lsocket.port = lport++;
  77.     if((fsocket.address = resolve(argv[1])) == 0){
  78.         printf(badhost,argv[1]);
  79.         return 1;
  80.     }
  81.     if(argc < 3)
  82.         fsocket.port = FTP_PORT;
  83.     else {
  84.         if((fsocket.port = atoi(argv[2])) == 0)
  85.             fsocket.port = FTP_PORT;
  86.         tos = get_tos(argv[3]);
  87.     }
  88.     /* Allocate a session control block */
  89.     if((s = newsession()) == NULLSESSION){
  90.         printf("Too many sessions\n");
  91.         return 1;
  92.     }
  93.     current = s;
  94.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  95.         strcpy(s->name,argv[1]);
  96.     s->type = FTP;
  97.     s->parse = ftpparse;
  98.  
  99.     /* Allocate an FTP control block */
  100.     if((ftp = ftp_create(0)) == NULLFTP){
  101.         s->type = FREE;
  102.         printf(nospace);
  103.         return 1;
  104.     }
  105.     ftp->state = COMMAND_STATE;
  106.     s->cb.ftp = ftp;    /* Downward link */
  107.     ftp->session = s;    /* Upward link */
  108.  
  109.     /* Now open the control connection */
  110.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  111.         0,ftpccr,NULLVFP,ftpccs,tos,(char *)ftp);
  112.     ftp->control = tcb;
  113.     go();
  114.     return 0;
  115. }
  116. /* Parse user FTP commands */
  117. int
  118. ftpparse(line,len)
  119. char *line;
  120. int16 len;
  121. {
  122.     struct mbuf *bp;
  123.  
  124.     if(current->cb.ftp->state != COMMAND_STATE){
  125.         /* The only command allowed in data transfer state is ABORT */
  126.         if(cmdparse(ftpabort,line) == -1){
  127.             printf("Transfer in progress; only ABORT is acceptable\n");
  128.         }
  129.         fflush(stdout);
  130.         return;
  131.     }
  132.  
  133.     /* Save it now because cmdparse modifies the original */
  134.     bp = qdata(line,len);
  135.  
  136.     if(cmdparse(ftpcmds,line) == -1){
  137.         /* Send it direct */
  138.         if(bp != NULLBUF)
  139.             send_tcp(current->cb.ftp->control,bp);
  140.         else
  141.             printf(nospace);
  142.     } else {
  143.         free_p(bp);
  144.     }
  145.     fflush(stdout);
  146. }
  147. /* Handle null line to avoid trapping on first command in table */
  148. static
  149. int
  150. donothing(argc,argv)
  151. int argc;
  152. char *argv[];
  153. {
  154.     return 0;
  155. }
  156. /* Translate 'cd' to 'cwd' for convenience */
  157. static
  158. int
  159. doftpcd(argc,argv)
  160. int argc;
  161. char *argv[];
  162. {
  163.     register struct ftp *ftp;
  164.  
  165.     ftp = current->cb.ftp;
  166.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  167. }
  168. /* Translate 'mkdir' to 'xmkd' for convenience */
  169. static
  170. int
  171. domkdir(argc,argv)
  172. int argc;
  173. char *argv[];
  174. {
  175.     register struct ftp *ftp;
  176.  
  177.     ftp = current->cb.ftp;
  178.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  179. }
  180. /* Translate 'rmdir' to 'xrmd' for convenience */
  181. static
  182. int
  183. dormdir(argc,argv)
  184. int argc;
  185. char *argv[];
  186. {
  187.     register struct ftp *ftp;
  188.  
  189.     ftp = current->cb.ftp;
  190.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  191. }
  192. /* Handle "type" command from user */
  193. static
  194. int
  195. dotype(argc,argv)
  196. int argc;
  197. char *argv[];
  198. {
  199.     register struct ftp *ftp;
  200.  
  201.     ftp = current->cb.ftp;
  202.     if(argc < 2){
  203.         switch(ftp->type){
  204.         case IMAGE_TYPE:
  205.             printf("Image\n");
  206.             break;
  207.         case ASCII_TYPE:
  208.             printf("Ascii\n");
  209.             break;
  210.         }
  211.         return 0;
  212.     }
  213.     switch(*argv[1]){
  214.     case 'i':
  215.     case 'b':
  216.         ftp->type = IMAGE_TYPE;
  217.         sndftpmsg(ftp,"TYPE I\r\n");
  218.         break;
  219.     case 'a':
  220.         ftp->type = ASCII_TYPE;
  221.         sndftpmsg(ftp,"TYPE A\r\n");
  222.         break;
  223.     case 'l':
  224.         if (argc > 2){
  225.             ftp->type = IMAGE_TYPE;
  226.             sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  227.             break;
  228.         }
  229.         /* no arg after TYPE L, drop in default
  230.            instead of generating BUS ERROR on Atari ST (and others) */
  231.     default:
  232.         printf("Invalid type %s\n",argv[1]);
  233.         return 1;
  234.     }
  235.     return 0;
  236. }
  237. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  238. static
  239. doget(argc,argv)
  240. int argc;
  241. char *argv[];
  242. {
  243.     void ftpdr(),ftpcds();
  244.     char *remotename,*localname;
  245.     register struct ftp *ftp;
  246.     char *mode;
  247.  
  248.     if((ftp = current->cb.ftp) == NULLFTP){
  249.         printf(notsess);
  250.         return 1;
  251.     }
  252.     remotename = argv[1];
  253.     if(argc < 3)
  254.         localname = remotename;
  255.     else
  256.         localname = argv[2];
  257.  
  258.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  259.         fclose(ftp->fp);
  260.  
  261.     if(ftp->type == IMAGE_TYPE)
  262.         mode = binmode[WRITE_BINARY];
  263.     else
  264.         mode = "w";
  265.  
  266.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  267.         printf(cantwrite,localname);
  268.         return 1;
  269.     }
  270.     ftp->state = RECEIVING_STATE;
  271.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  272.  
  273.     /* Generate the command to start the transfer */
  274.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  275. }
  276. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  277. static
  278. dolist(argc,argv)
  279. int argc;
  280. char *argv[];
  281. {
  282.     void ftpdr(),ftpcds();
  283.     register struct ftp *ftp;
  284.  
  285.     if((ftp = current->cb.ftp) == NULLFTP){
  286.         printf(notsess);
  287.         return 1;
  288.     }
  289.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  290.         fclose(ftp->fp);
  291.  
  292.     if(argc < 3){
  293.         ftp->fp = stdout;
  294.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  295.         printf(cantwrite,argv[2]);
  296.         return 1;
  297.     }
  298.     ftp->state = RECEIVING_STATE;
  299.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  300.     /* Generate the command to start the transfer
  301.      * It's done this way to avoid confusing the 4.2 FTP server
  302.      * if there's no argument
  303.      */
  304.     if(argc > 1)
  305.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  306.     else
  307.         return sndftpmsg(ftp,"LIST\r\n");
  308. }
  309. /* Abbreviated (name only) list of remote directory.
  310.  * Syntax: ls <remote directory/file> [<local name>]
  311.  */
  312. static
  313. dols(argc,argv)
  314. int argc;
  315. char *argv[];
  316. {
  317.     void ftpdr(),ftpcds();
  318.     register struct ftp *ftp;
  319.  
  320.     if((ftp = current->cb.ftp) == NULLFTP){
  321.         printf(notsess);
  322.         return 1;
  323.     }
  324.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  325.         fclose(ftp->fp);
  326.  
  327.     if(argc < 3){
  328.         ftp->fp = stdout;
  329.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  330.         printf(cantwrite,argv[2]);
  331.         return 1;
  332.     }
  333.     ftp->state = RECEIVING_STATE;
  334.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  335.     /* Generate the command to start the transfer */
  336.     if(argc > 1)
  337.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  338.     else
  339.         return sndftpmsg(ftp,"NLST\r\n");
  340. }
  341. /* Start transmit. Syntax: put <local name> [<remote name>] */
  342. static
  343. doput(argc,argv)
  344. int argc;
  345. char *argv[];
  346. {
  347.     void ftpdt(),ftpcds();
  348.     char *remotename,*localname;
  349.     char *mode;
  350.     struct ftp *ftp;
  351.  
  352.     if((ftp = current->cb.ftp) == NULLFTP){
  353.         printf(notsess);
  354.         return 1;
  355.     }
  356.     localname = argv[1];
  357.     if(argc < 3)
  358.         remotename = localname;
  359.     else
  360.         remotename = argv[2];
  361.  
  362.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  363.         fclose(ftp->fp);
  364.  
  365.     if(ftp->type == IMAGE_TYPE)
  366.         mode = binmode[READ_BINARY];
  367.     else
  368.         mode = "r";
  369.  
  370.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  371.         printf(cantread,localname);
  372.         return 1;
  373.     }
  374.     ftp->state = SENDING_STATE;
  375.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  376.  
  377.     /* Generate the command to start the transfer */
  378.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  379. }
  380. #if (defined(MSDOS) || defined(ATARI_ST))
  381. /* Transmit tree. Syntax: tput <local spec> */
  382. static
  383. dotput(argc,argv)
  384. int argc;
  385. char *argv[];
  386. {
  387.     char *p;
  388.     struct ftp *ftp;
  389.     struct ftp_list *fl,**flp;
  390.     int num;
  391.     char localspec[128],localname[128],name[15];
  392.     void ftp_dellist();
  393.     int isdir(),iswild();
  394.     char *basename();
  395.  
  396.     if((ftp = current->cb.ftp) == NULLFTP){
  397.         printf(notsess);
  398.         return 1;
  399.     }
  400.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  401.         fclose(ftp->fp);
  402.     ftp->fp = NULLFILE;
  403.     ftp_dellist(ftp);
  404.  
  405.     /* Create a (dummy) ftp_list node containing the spec */
  406.     if ((fl = (struct ftp_list *) calloc(1,sizeof(struct ftp_list) + strlen(argv[1]))) == NULL) {
  407.         printf(nospace);
  408.         return 1;
  409.     }
  410.     strcpy(fl->localname,argv[1]);
  411.     cleanup_fname(fl->localname);
  412.  
  413.     ftp->ftp_list = fl;            /* 1st item of the list */
  414.  
  415.     /* scan the ever-growing list until all directories are processed */
  416.     for (flp = &fl->next; fl != NULLFTPLIST; fl = fl->next) {
  417.         strcpy(localspec,fl->localname);
  418.         if ((fl->isdir = isdir(localspec)) != 0) /* directory - take all it's files */
  419.         strcat(localspec,"/*.*");
  420.         cleanup_fname(localspec);
  421.         if (iswild(localspec)) {
  422.         strcpy(localname,localspec);
  423.         p = basename(localname);
  424.         /* scan wildcard pattern, append files to end of list */
  425.         for (num = 0; filedir(localspec,num,name), name[0] != '\0'; num++) {
  426.             if (name[0] == '.')
  427.             continue;        /* ignore '.' and '..' */
  428.             strcpy(p,name);        /* append name to dir */
  429.             if ((*flp = (struct ftp_list *) calloc(1,sizeof(struct ftp_list) + strlen(localname))) == NULL) {
  430.             printf(nospace);
  431.             ftp_dellist(ftp);
  432.             return 1;
  433.             }
  434.             strcpy((*flp)->localname,localname);
  435.             flp = &(*flp)->next;
  436.         }
  437.         }
  438.     }
  439.     /* Remove the dummy 1st element (the spec) if not a directory */
  440.     if (!(fl = ftp->ftp_list)->isdir && iswild(fl->localname)) {
  441.         ftp->ftp_list = fl->next;
  442.         free(fl);
  443.     }
  444.     if (ftp->ftp_list == NULLFTPLIST) {
  445.         printf("No file %s\n",argv[1]);
  446.         return 0;
  447.     }
  448.     ftp->xatr = (argc > 2);        /* extra arg = use XATR */
  449.     return donextput(ftp);        /* trigger the multi-store op */
  450. }
  451. static
  452. donextput(ftp)
  453. register struct ftp *ftp;
  454. {
  455.     struct ftp_list *fl;
  456.     char *mode;
  457.     int rv;
  458.     void ftpdt(),ftpcds();
  459.  
  460.     if(ftp->state != COMMAND_STATE)
  461.         return 1;
  462.  
  463.     if((fl = ftp->ftp_list) == NULLFTPLIST)
  464.         return -1;
  465.  
  466.     ftp->ftp_list = fl->next;
  467.  
  468.     if(fl->isdir) {            /* is it a directory? */
  469.         rv = sndftpmsg(ftp,"XMKD %s\r\n",fl->localname);
  470.     } else {
  471.         if(ftp->type == IMAGE_TYPE)
  472.         mode = binmode[READ_BINARY];
  473.         else
  474.         mode = "r";
  475.  
  476.         if((ftp->fp = fopen(fl->localname,mode)) == NULLFILE){
  477.         printf(cantread,fl->localname);
  478.         return 1;
  479.         }
  480.         ftp->state = SENDING_STATE;
  481.         ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  482.  
  483.         /* Generate the command to start the transfer */
  484.         rv = sndftpmsg(ftp,"STOR %s\r\n",fl->localname);
  485.  
  486. #if (defined (ATARI_ST))
  487.         /* When required, send an XATR command (set attibutes) */
  488.         if (ftp->xatr) {
  489.         int attr;
  490.         unsigned long datime;
  491.         unsigned int info[2];
  492.         char buf[26];
  493.  
  494.         if ((attr = Fattrib(fl->localname,0,0)) < 0) /* get attribs */
  495.             attr = 0;
  496.  
  497.         Fdatime(info,fileno(ftp->fp),0);    /* get date/time */
  498. # if (defined(MWC) && (MWC < 306))
  499.         datime = info[0] | ((long) info[1] << Sixteen);
  500. # else
  501.         datime = info[0] | ((long) info[1] << 16);
  502. # endif
  503.  
  504.         sprintf(buf,"%u,%u,%u,%u,%u,%u",
  505.             hibyte(hiword(datime)),
  506.             lobyte(hiword(datime)),
  507.             hibyte(loword(datime)),
  508.             lobyte(loword(datime)),
  509.             hibyte(attr),
  510.             lobyte(attr));
  511.  
  512.         rv |= sndftpmsg(ftp,"XATR %s\r\n",buf);
  513.         }
  514. #endif
  515.     }
  516.  
  517.     free(fl);
  518.     return rv;
  519. }
  520. #endif
  521. /* Abort a GET or PUT operation in progress. Note: this will leave
  522.  * the partial file on the local or remote system
  523.  */
  524. doabort(argc,argv)
  525. int argc;
  526. char *argv[];
  527. {
  528.     register struct ftp *ftp;
  529.  
  530.     ftp = current->cb.ftp;
  531.  
  532.     /* Close the local file */
  533.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  534.         fclose(ftp->fp);
  535.     ftp->fp = NULLFILE;
  536. #if (defined(MSDOS) || defined(ATARI_ST))
  537.     ftp_dellist(ftp);
  538. #endif
  539.     switch(ftp->state){
  540.     case SENDING_STATE:
  541.         /* Send a premature EOF.
  542.          * Unfortunately we can't just reset the connection
  543.          * since the remote side might end up waiting forever
  544.          * for us to send something.
  545.          */
  546.         close_tcp(ftp->data);
  547.         printf("Put aborted\n");
  548.         break;
  549.     case RECEIVING_STATE:
  550.         /* Just exterminate the data channel TCB; this will
  551.          * generate a RST on the next data packet which will
  552.          * abort the sender
  553.          */
  554.         del_tcp(ftp->data);
  555.         ftp->data = NULLTCB;
  556.         printf("Get aborted\n");
  557.         break;
  558.     }
  559.     ftp->state = COMMAND_STATE;
  560.     fflush(stdout);
  561. }
  562. /* create data port, and send PORT message */
  563. static
  564. ftpsetup(ftp,recv,send,state)
  565. struct ftp *ftp;
  566. void (*send)();
  567. void (*recv)();
  568. void (*state)();
  569. {
  570.     struct socket lsocket;
  571.     struct mbuf *bp;
  572.  
  573.     lsocket.address = ip_addr;
  574.     lsocket.port = lport++;
  575.  
  576.     /* Compose and send PORT a,a,a,a,p,p message */
  577.  
  578.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  579.         printf(nospace);
  580.         return;
  581.     }
  582.     /* I know, this looks gross, but it works! */
  583.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  584.         hibyte(hiword(lsocket.address)),
  585.         lobyte(hiword(lsocket.address)),
  586.         hibyte(loword(lsocket.address)),
  587.         lobyte(loword(lsocket.address)),
  588.         hibyte(lsocket.port),
  589.         lobyte(lsocket.port));
  590.     bp->cnt = strlen(bp->data);
  591.     send_tcp(ftp->control,bp);
  592.  
  593.     /* Post a listen on the data connection */
  594.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  595.         recv,send,state,ftp->control->tos,(char *)ftp);
  596. }
  597. /* FTP Client Control channel Receiver upcall routine */
  598. void
  599. ftpccr(tcb,cnt)
  600. register struct tcb *tcb;
  601. int16 cnt;
  602. {
  603.     struct mbuf *bp;
  604.     struct ftp *ftp;
  605.     extern int flowctrl,ttydriv();
  606.  
  607.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  608.         /* Unknown connection; kill it */
  609.         close_tcp(tcb);
  610.         return;
  611.     }
  612.     /* Hold output if we're not the current session, and while typing */
  613.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp
  614.        || (flowctrl && ttydriv(-1,NULL)))
  615.         return;
  616.  
  617.     if(recv_tcp(tcb,&bp,cnt) > 0){
  618.         while(bp != NULLBUF){
  619.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  620.             if(current->record != NULLFILE)
  621.                 fwrite(bp->data,1,(unsigned)bp->cnt,current->record);
  622.             bp = free_mbuf(bp);
  623.         }
  624.         fflush(stdout);
  625.     }
  626. #if (defined(MSDOS) || defined(ATARI_ST))
  627.     if (ftp->state == COMMAND_STATE)
  628.         donextput(ftp);
  629. #endif
  630. }
  631.  
  632. /* FTP Client Control channel State change upcall routine */
  633. static
  634. void
  635. ftpccs(tcb,old,new)
  636. register struct tcb *tcb;
  637. char old,new;
  638. {
  639.     void ftp_delete();
  640.     struct ftp *ftp;
  641.     char notify = 0;
  642.     extern char *tcpstates[];
  643.     extern char *reasons[];
  644.     extern char *unreach[];
  645.     extern char *exceed[];
  646.  
  647.     /* Can't add a check for unknown connection here, it would loop
  648.      * on a close upcall! We're just careful later on.
  649.      */
  650.     ftp = (struct ftp *)tcb->user;
  651.  
  652.     if(current != NULLSESSION && current->cb.ftp == ftp)
  653.         notify = 1;
  654.  
  655.     switch(new){
  656.     case CLOSE_WAIT:
  657.         if(notify)
  658.             printf("%s\n",tcpstates[new]);
  659.         close_tcp(tcb);
  660.         break;
  661.     case CLOSED:    /* heh heh */
  662.         if(notify){
  663.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  664.             if(tcb->reason == NETWORK){
  665.                 switch(tcb->type){
  666.                 case DEST_UNREACH:
  667.                     printf(": %s unreachable",unreach[tcb->code]);
  668.                     break;
  669.                 case TIME_EXCEED:
  670.                     printf(": %s time exceeded",exceed[tcb->code]);
  671.                     break;
  672.                 }
  673.             }
  674.             printf(")\n");
  675.             cmdmode();
  676.         }
  677.         del_tcp(tcb);
  678.         if(ftp != NULLFTP)
  679.             ftp_delete(ftp);
  680.         break;
  681.     default:
  682.         if(notify)
  683.             printf("%s\n",tcpstates[new]);
  684.         break;
  685.     }
  686.     if(notify)
  687.         fflush(stdout);
  688. }
  689. /* FTP Client Data channel State change upcall handler */
  690. static
  691. void
  692. ftpcds(tcb,old,new)
  693. struct tcb *tcb;
  694. char old,new;
  695. {
  696.     struct ftp *ftp;
  697.     unsigned long xfertime;
  698.  
  699.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  700.         /* Unknown connection, kill it */
  701.         close_tcp(tcb);
  702.         return;
  703.     }
  704.     switch(new){
  705.     case ESTABLISHED:
  706.         /* Start the timer so we can print the effective speed */
  707.         ftp->xfertime.start = MAX_TIME;
  708.         start_timer(&ftp->xfertime);
  709.         break;
  710.  
  711.     case FINWAIT2:
  712.     case TIME_WAIT:
  713.         if(ftp->state == SENDING_STATE){
  714.             /* We've received an ack of our FIN, so
  715.              * return to command mode
  716.              */
  717.             ftp->state = COMMAND_STATE;
  718.             if(current != NULLSESSION && current->cb.ftp == ftp){
  719.                 printf("Put complete, %lu bytes sent",
  720.                     tcb->snd.una - tcb->iss - 2);
  721.                 if ((xfertime = TICK2MS(ftp->xfertime.start - ftp->xfertime.count)) != 0)
  722.                     printf(" in %lu seconds (%lu bits/s)",
  723.                         (xfertime + 500L) / 1000L,
  724.                         ((unsigned long) 8000 * (tcb->snd.una - tcb->iss - 2)) / xfertime);
  725.                 stop_timer(&ftp->xfertime);
  726.                 printf("\n");
  727.                 fflush(stdout);
  728.             }
  729.         }
  730.         break;
  731.     case CLOSE_WAIT:
  732.         close_tcp(tcb);
  733.         if(ftp->state == RECEIVING_STATE){
  734.             /* End of file received on incoming file */
  735. #ifdef    CPM
  736.             if(ftp->type == ASCII_TYPE)
  737.                 putc(CTLZ,ftp->fp);
  738. #endif
  739.             if(ftp->fp != stdout)
  740.                 fclose(ftp->fp);
  741.             ftp->fp = NULLFILE;
  742.             ftp->state = COMMAND_STATE;
  743.             if(current != NULLSESSION && current->cb.ftp == ftp){
  744.                 printf("Get complete, %lu bytes received",
  745.                     tcb->rcv.nxt - tcb->irs - 2);
  746.                 if ((xfertime = TICK2MS(ftp->xfertime.start - ftp->xfertime.count)) != 0)
  747.                     printf(" in %lu seconds (%lu bits/s)",
  748.                         (xfertime + 500L) / 1000L,
  749.                         ((unsigned long) 8000 * (tcb->rcv.nxt - tcb->irs - 2)) / xfertime);
  750.                 stop_timer(&ftp->xfertime);
  751.                 printf("\n");
  752.                 fflush(stdout);
  753.             }
  754.         }
  755.         break;
  756.     case CLOSED:
  757.         ftp->data = NULLTCB;
  758.         del_tcp(tcb);
  759.         break;
  760.     }
  761. }
  762. /* Send a message on the control channel */
  763. /*VARARGS*/
  764. static
  765. int
  766. sndftpmsg(ftp,fmt,arg)
  767. struct ftp *ftp;
  768. char *fmt;
  769. char *arg;
  770. {
  771.     struct mbuf *bp;
  772.     int16 len;
  773.  
  774.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  775.     if((bp = alloc_mbuf(len)) == NULLBUF){
  776.         printf(nospace);
  777.         return 1;
  778.     }
  779.     sprintf(bp->data,fmt,arg);
  780.     bp->cnt = strlen(bp->data);
  781.     send_tcp(ftp->control,bp);
  782.     return 0;
  783. }
  784.